﻿using Futajma.MigrationStudio.DataDefinition;
using Futajma.MigrationStudio.GenericPersistance;
using Futajma.MigrationStudio.GenericPersistance.Data;
using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;

namespace Futajma.MigrationStudio.GenericPersistance
{



    public abstract class ProjectPersistence : IProjectPersistence
    {
        // Xml tag for node, e.g. 'node' in case of <node></node>
        protected const string XmlNodeTag = "node";

        // Xml attributes for node e.g. <node text="Asia" tag="" 
        // imageindex="1"></node>
        protected const string XmlNodeTextAtt = "text";
        protected const string XmlNodeTagAtt = "tag";
        protected const string XmlNodeImageIndexAtt = "imageindex";
        protected const string XmlNodeSelectedImageIndexAtt = "selectedimageindex";

        //protected const string XmlNodeSelectedItemId = "SelectedItemId";

        protected const string XmlNodeProjectDefinitionVersion = "ProjectDefinitionVersion";

        public event ProgressUpdate ProgressUpdate;

        public event ProgressInit ProgressInit;

        public event ProgressUpdateDone ProgressUpdateDone;

        long progressCount = 0;

        public static void CopyStream(Stream source, Stream destination)
        {

            byte[] buffer = new byte[32768];

            int bytesRead;

            do
            {

                bytesRead = source.Read(buffer, 0, buffer.Length);

                destination.Write(buffer, 0, bytesRead);

            } while (bytesRead != 0);

        }

        //protected void ProgressStreamPositionChanged(object sender, long newPosition, long length)
        //{
        //    if (DownloadUpdate != null)
        //    {
        //        int promile = (int)(length / 1000);
        //        int step = (int)(newPosition / 1000);

        //        DownloadUpdate(promile, step);
        //    }
        //}

        XmlReaderSettings GetXmlReaderSettings()
        {
            var xmlReaderSettings = new XmlReaderSettings();
            xmlReaderSettings.CheckCharacters = false;
            xmlReaderSettings.IgnoreComments = true;
            xmlReaderSettings.IgnoreProcessingInstructions = true;
            xmlReaderSettings.IgnoreWhitespace = true;
            return xmlReaderSettings;
        }

        protected void DeserializeTreeView(TreeView treeView, XmlReader reader, Futajma.MigrationStudio.GenericImporter.IImporter sourceImporter)
        {

            try
            {
                treeView.Nodes.Clear();
                treeView.BeginUpdate();

                TreeNode parentNode = null;
                while (reader.Read())
                {
                    if (reader.NodeType == XmlNodeType.Element)
                    {
                        if (reader.Name == XmlNodeTag)
                        {
                            TreeNode newNode = new TreeNode();
                            bool isEmptyElement = reader.IsEmptyElement;

                            // loading node attributes
                            int attributeCount = reader.AttributeCount;
                            if (attributeCount > 0)
                            {
                                for (int i = 0; i < attributeCount; i++)
                                {
                                    reader.MoveToAttribute(i);
                                    SetAttributeValue(newNode,
                                                 reader.Name, reader.Value);
                                }
                            }
                            // add new node to Parent Node or TreeView
                            if (parentNode != null)
                                parentNode.Nodes.Add(newNode);
                            else
                                treeView.Nodes.Add(newNode);

                            // making current node 'ParentNode' if its not empty
                            if (!isEmptyElement)
                            {
                                parentNode = newNode;
                            }

                            reader.Read();

                            if (reader.Name == XmlNodeTagAtt)
                            {
                                reader.Read();
                                //var value = reader.ReadOuterXml();

                                var value = reader.ReadSubtree();

                                XmlSerializer serializer = sourceImporter.GetXmlSerializer();//new XmlSerializer(typeof(DataNode));

                                //using (XmlReader serialReader = XmlReader.Create(stream, GetXmlReaderSettings()))
                                //{
                                newNode.Tag = (DataNode)serializer.Deserialize(value);
                                //}

                                //using (StringReader writer = new StringReader(value))
                                //{
                                //    newNode.Tag = (DataNode)serializer.Deserialize(writer);
                                //}
                            }
                            else
                            {
                                throw new InvalidDataException("XML Node 'tag' is required");
                            }
                        }
                    }

                    // moving up to in TreeView if end tag is encountered
                    else if (reader.NodeType == XmlNodeType.EndElement)
                    {
                        if (reader.Name == XmlNodeTag)
                        {
                            parentNode = parentNode.Parent;
                        }
                    }
                    else if (reader.NodeType == XmlNodeType.XmlDeclaration)
                    {
                        //Ignore Xml Declaration                    
                    }
                    else if (reader.NodeType == XmlNodeType.None)
                    {
                        return;
                    }
                    else if (reader.NodeType == XmlNodeType.Text)
                    {
                        parentNode.Nodes.Add(reader.Value);
                    }
                }
            }
            finally
            {
                // enabling redrawing of treeview after all nodes are added
                treeView.EndUpdate();
            }

        }

        protected void DeserializeTreeView(TreeView treeView, Stream stream, Futajma.MigrationStudio.GenericImporter.IImporter sourceImporter)
        {
            using (XmlReader reader = XmlReader.Create(stream, GetXmlReaderSettings()))
            {
                try
                {
                    treeView.Nodes.Clear();
                    treeView.BeginUpdate();

                    TreeNode parentNode = null;
                    while (reader.Read())
                    {
                        if (reader.NodeType == XmlNodeType.Element)
                        {
                            if (reader.Name == XmlNodeTag)
                            {
                                TreeNode newNode = new TreeNode();
                                bool isEmptyElement = reader.IsEmptyElement;

                                // loading node attributes
                                int attributeCount = reader.AttributeCount;
                                if (attributeCount > 0)
                                {
                                    for (int i = 0; i < attributeCount; i++)
                                    {
                                        reader.MoveToAttribute(i);
                                        SetAttributeValue(newNode,
                                                     reader.Name, reader.Value);
                                    }
                                }
                                // add new node to Parent Node or TreeView
                                if (parentNode != null)
                                    parentNode.Nodes.Add(newNode);
                                else
                                    treeView.Nodes.Add(newNode);

                                // making current node 'ParentNode' if its not empty
                                if (!isEmptyElement)
                                {
                                    parentNode = newNode;
                                }

                                reader.Read();

                                if (reader.Name == XmlNodeTagAtt)
                                {
                                    reader.Read();
                                    //var value = reader.ReadOuterXml();

                                    var value = reader.ReadSubtree();

                                    XmlSerializer serializer = sourceImporter.GetXmlSerializer();//new XmlSerializer(typeof(DataNode));

                                    //using (XmlReader serialReader = XmlReader.Create(stream, GetXmlReaderSettings()))
                                    //{
                                    newNode.Tag = (DataNode)serializer.Deserialize(value);
                                    //}

                                    //using (StringReader writer = new StringReader(value))
                                    //{
                                    //    newNode.Tag = (DataNode)serializer.Deserialize(writer);
                                    //}
                                }
                                else
                                {
                                    throw new InvalidDataException("XML Node 'tag' is required");
                                }
                            }
                        }

                        // moving up to in TreeView if end tag is encountered
                        else if (reader.NodeType == XmlNodeType.EndElement)
                        {
                            if (reader.Name == XmlNodeTag)
                            {
                                parentNode = parentNode.Parent;
                            }
                        }
                        else if (reader.NodeType == XmlNodeType.XmlDeclaration)
                        {
                            //Ignore Xml Declaration                    
                        }
                        else if (reader.NodeType == XmlNodeType.None)
                        {
                            return;
                        }
                        else if (reader.NodeType == XmlNodeType.Text)
                        {
                            parentNode.Nodes.Add(reader.Value);
                        }
                    }
                }
                finally
                {
                    // enabling redrawing of treeview after all nodes are added
                    treeView.EndUpdate();
                }
            }
        }

        /// <span class="code-SummaryComment"><summary>
        /// Used by Deserialize method for setting properties of 
        /// TreeNode from xml node attributes
        /// <span class="code-SummaryComment"></summary>
        protected void SetAttributeValue(TreeNode node, string propertyName, string value)
        {
            if (propertyName == XmlNodeTextAtt)
            {
                node.Text = value;
            }
            else if (propertyName == XmlNodeImageIndexAtt)
            {
                node.ImageIndex = int.Parse(value);
            }
            else if (propertyName == XmlNodeSelectedImageIndexAtt)
            {
                node.SelectedImageIndex = int.Parse(value);
            }
            //else if (propertyName == XmlNodeTagAtt)
            //{
            //    XmlSerializer serializer = new XmlSerializer(typeof(DataNode));

            //    using (StringReader writer = new StringReader(value))
            //    {
            //        node.Tag = (DataNode)serializer.Deserialize(writer);
            //    }

            //    node.Tag = value;
            //}
        }

        protected StringBuilder SerializeTreeView(TreeView treeView, string startElementName, Futajma.MigrationStudio.GenericImporter.IImporter sourceImporter)
        {
            //XmlWriterSettings settings = new XmlWriterSettings();
            //settings.Encoding = new UnicodeEncoding(false, false); // no BOM in a .NET string
            //settings.Indent = true;
            //settings.OmitXmlDeclaration = true;
            //settings.CheckCharacters = false;

            StringBuilder sb = new StringBuilder();
            XmlWriter writer = XmlWriter.Create(sb, GetXmlWriterSettings());
            //XmlTextWriter writer = new XmlTextWriter(fileName,
            //                              System.Text.Encoding.ASCII);
            // writing the xml declaration tag
            writer.WriteStartDocument();
            //textWriter.WriteRaw("\r\n");
            // writing the main tag that encloses all node tags
            writer.WriteStartElement(startElementName);

            // save the nodes, recursive method
            SaveNodes(treeView.Nodes, writer, sourceImporter);

            writer.WriteEndElement();

            writer.Close();
            return sb;
        }

        protected void SaveNodes(TreeNodeCollection nodesCollection, XmlWriter textWriter, Futajma.MigrationStudio.GenericImporter.IImporter sourceImporter)
        {
            for (int i = 0; i < nodesCollection.Count; i++)
            {
                TreeNode node = nodesCollection[i];
                textWriter.WriteStartElement(XmlNodeTag);
                textWriter.WriteAttributeString(XmlNodeTextAtt, node.Text);
                textWriter.WriteAttributeString(XmlNodeImageIndexAtt, node.ImageIndex.ToString());
                textWriter.WriteAttributeString(XmlNodeSelectedImageIndexAtt, node.ImageIndex.ToString());

                textWriter.WriteStartElement(XmlNodeTagAtt);

                if (node.Tag != null)
                {
                    //XmlWriterSettings settings = new XmlWriterSettings();
                    //settings.Encoding = new UnicodeEncoding(false, false); // no BOM in a .NET string
                    //settings.Indent = true;
                    //settings.OmitXmlDeclaration = true;
                    //settings.CheckCharacters = false;

                    XmlSerializer serializer = sourceImporter.GetXmlSerializer();//new XmlSerializer(typeof(DataNode));

                    StringBuilder sb = new StringBuilder();
                    using (XmlWriter writer = XmlWriter.Create(sb, GetXmlWriterSettings()))
                    {
                        serializer.Serialize(writer, node.Tag);

                        textWriter.WriteRaw(sb.ToString());
                    }
                }

                textWriter.WriteEndElement();

                if (node.Nodes.Count > 0)
                {
                    SaveNodes(node.Nodes, textWriter, sourceImporter);
                }
                textWriter.WriteEndElement();

                SendProgressUpdate(++progressCount);
            }
        }

        // legacy support
        private void LoadInitialProjectVersion(XmlDocument doc, TreeView sourceTreeView, TreeView targetTreeView, ProjectData projectData)
        {
            Debug.WriteLine("Create ProjectParameter data:" + DateTime.Now.ToLongTimeString());

            XmlNode selectedItemId = doc.DocumentElement.SelectSingleNode("SelectedItemId");

            projectData.ProjectParameter = new ProjectParameter();
            projectData.ProjectParameter.SelectedSharePointItemId = int.Parse(selectedItemId.InnerText);
            projectData.ProjectParameter.ProjectVersion = 0;
            projectData.ProjectParameter.ProjectIsCommited = false;

            var stream = new MemoryStream();
            using (XmlWriter writer = XmlWriter.Create(stream, GetXmlWriterSettings(false)))
            {
                XmlNode sourceNode = doc.DocumentElement.SelectSingleNode("SourceTreeView");
                sourceNode.WriteTo(writer);
                writer.Flush();
                stream.Position = 0;
            }

            Debug.WriteLine("Source tree extracted, start deserialization:" + DateTime.Now.ToLongTimeString());
            DeserializeTreeView(sourceTreeView, stream, projectData.Importer);
            Debug.WriteLine("Desetialization done, start load target tree:" + DateTime.Now.ToLongTimeString());
            stream = new MemoryStream();
            using (XmlWriter writer = XmlWriter.Create(stream, GetXmlWriterSettings(false)))
            {
                XmlNode sourceNode = doc.DocumentElement.SelectSingleNode("TargetTreeView");
                sourceNode.WriteTo(writer);
                writer.Flush();
                stream.Position = 0;
            }

            Debug.WriteLine("Target tree extracted, start deserialization:" + DateTime.Now.ToLongTimeString());
            DeserializeTreeView(targetTreeView, stream, projectData.Importer);
            //Debug.WriteLine("Desetialization done, start load actions:" + DateTime.Now.ToLongTimeString());
            //stream = new MemoryStream();
            //using (XmlWriter writer = XmlWriter.Create(stream))
            //{
            //    XmlNode sourceNode = doc.DocumentElement.SelectSingleNode("ArrayOfAction");
            //    if (sourceNode != null)
            //    {
            //        sourceNode.WriteTo(writer);
            //        writer.Flush();
            //        stream.Position = 0;
            //        //projectData.ActionItemHandler.LoadXml(stream);
            //    }
            //}

            Debug.WriteLine("Load governance data:" + DateTime.Now.ToLongTimeString());
            stream = new MemoryStream();
            using (XmlWriter writer = XmlWriter.Create(stream))
            {
                XmlNode sourceNode = doc.DocumentElement.SelectSingleNode("GovernanceData");
                if (sourceNode != null)
                {
                    sourceNode.WriteTo(writer);
                    writer.Flush();
                    stream.Position = 0;
                    projectData.GovernanceData = GovernanceData.LoadXml(stream);
                }
            }

            Debug.WriteLine("Load Sitecollection templates:" + DateTime.Now.ToLongTimeString());
            stream = new MemoryStream();
            using (XmlWriter writer = XmlWriter.Create(stream))
            {
                XmlNode sourceNode = doc.DocumentElement.SelectSingleNode("ArrayOfSiteCollectionTemplate");
                if (sourceNode != null)
                {
                    sourceNode.WriteTo(writer);
                    writer.Flush();
                    stream.Position = 0;
                    Data.TemplateDefinition.Instance.Clear();
                    Data.TemplateDefinition.Instance.SiteCollectionDefinitions = TemplateDefinition.LoadXml(stream);
                }
            }


        }

        protected void LoadData(Stream inStream, TreeView sourceTreeView, TreeView targetTreeView, ProjectData projectData)
        {
            Debug.WriteLine("Start load document:" + DateTime.Now.ToLongTimeString());

            using (XmlReader reader = XmlReader.Create(inStream, GetXmlReaderSettings()))
            {

                while (reader.Read())
                {
                    if (reader.NodeType == XmlNodeType.Element)
                    {
                        #region Check legacy support
                        if (reader.Name == XmlNodeProjectDefinitionVersion)
                        {

                            var currentVersion = reader.ReadInnerXml();
                            if (string.IsNullOrEmpty(currentVersion) || !ProjectData.CheckCompatibility(currentVersion))
                            {
                                if (string.IsNullOrEmpty(currentVersion))
                                {
                                    // migrate initial version
                                    if (MessageBox.Show("The selected Migration Project is outdated, a migration will be executed during the load. Press OK to start the migration or Cancel and the project will not load.", "Alert", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation) == DialogResult.OK)
                                    {
                                        Debug.WriteLine("Initial project structure migration start");

                                        //LoadInitialProjectVersion(doc, sourceTreeView, targetTreeView, projectData);

                                        Debug.WriteLine("Initial project structure migration done");
                                    }
                                    else
                                    {
                                        throw new WrongVersionFileException(string.Format("Version {0} expected, but file version is not available.", ProjectData.CurrentDefinitionVersion));
                                    }
                                }
                                else
                                {
                                    // currentVersion.InnerText
                                    throw new WrongVersionFileException(string.Format("Version {0} expected, but version {0} provided and are not compatible.", ProjectData.CurrentDefinitionVersion, currentVersion));
                                }
                            }

                        }
                        #endregion

                        if (reader.Name == "ProjectParameter")
                        {
                            var value = reader.ReadSubtree();
                            XmlSerializer serializer = new XmlSerializer(typeof(ProjectParameter));
                            projectData.ProjectParameter = (ProjectParameter)(serializer.Deserialize(value));
                        }

                        if (reader.Name == "SourceTreeView")
                        {
                            var value = reader.ReadSubtree();
                            DeserializeTreeView(sourceTreeView, value, projectData.Importer);
                        }

                        if (reader.Name == "TargetTreeView")
                        {
                            var value = reader.ReadSubtree();
                            DeserializeTreeView(targetTreeView, value, projectData.Importer);
                        }

                        if (reader.Name == "ArrayOfSiteCollectionTemplate")
                        {
                            var value = reader.ReadSubtree();
                            XmlSerializer serializer = new XmlSerializer(typeof(List<SiteCollectionTemplate>));
                            TemplateDefinition.Instance.Clear();
                            TemplateDefinition.Instance.SiteCollectionDefinitions = (List<SiteCollectionTemplate>)serializer.Deserialize(value);
                        }

                        if (reader.Name == "GovernanceData")
                        {
                            var value = reader.ReadSubtree();
                            XmlSerializer serializer = new XmlSerializer(typeof(GovernanceData));
                            projectData.GovernanceData = (GovernanceData)serializer.Deserialize(value);
                        }
                    }
                }
            }
        }

        //protected void LoadData1(Stream inStream, TreeView sourceTreeView, TreeView targetTreeView, ProjectData projectData)
        //{
        //    Debug.WriteLine("Start load document:" + DateTime.Now.ToLongTimeString());
        //    XmlDocument doc = new XmlDocument();
        //    doc.PreserveWhitespace = true;
        //    doc.Load(inStream);
        //    Debug.WriteLine("Start document loaded:" + DateTime.Now.ToLongTimeString());

        //    XmlNode currentVersion = doc.DocumentElement.SelectSingleNode(XmlNodeProjectDefinitionVersion);

        //    if (currentVersion == null || currentVersion.InnerText == null || !ProjectData.CheckCompatibility(currentVersion.InnerText))
        //    {
        //        if (currentVersion == null || currentVersion.InnerText == null)
        //        {
        //            // migrate initial version
        //            if (MessageBox.Show("The selected Migration Project is outdated, a migration will be executed during the load. Press OK to start the migration or Cancel and the project will not load.", "Alert", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation) == DialogResult.OK)
        //            {
        //                Debug.WriteLine("Initial project structure migration start");
        //                LoadInitialProjectVersion(doc, sourceTreeView, targetTreeView, projectData);
        //                Debug.WriteLine("Initial project structure migration done");
        //            }
        //            else
        //            {
        //                throw new WrongVersionFileException(string.Format("Version {0} expected, but file version is not available.", ProjectData.CurrentDefinitionVersion));
        //            }
        //        }
        //        else
        //        {
        //            // currentVersion.InnerText
        //            throw new WrongVersionFileException(string.Format("Version {0} expected, but version {0} provided and are not compatible.", ProjectData.CurrentDefinitionVersion, currentVersion.InnerText));
        //        }
        //    }
        //    else
        //    {

        //        Debug.WriteLine("ProjectParameter data:" + DateTime.Now.ToLongTimeString());
        //        var stream = new MemoryStream();
        //        using (XmlWriter writer = XmlWriter.Create(stream))
        //        {
        //            XmlNode sourceNode = doc.DocumentElement.SelectSingleNode("ProjectParameter");
        //            if (sourceNode != null)
        //            {
        //                sourceNode.WriteTo(writer);
        //                writer.Flush();
        //                stream.Position = 0;
        //                projectData.ProjectParameter = ProjectParameter.LoadXml(stream);
        //            }
        //        }


        //        stream = new MemoryStream();
        //        using (XmlWriter writer = XmlWriter.Create(stream, GetXmlWriterSettings(false)))
        //        {
        //            XmlNode sourceNode = doc.DocumentElement.SelectSingleNode("SourceTreeView");
        //            sourceNode.WriteTo(writer);
        //            writer.Flush();
        //            stream.Position = 0;
        //        }

        //        Debug.WriteLine("Source tree extracted, start deserialization:" + DateTime.Now.ToLongTimeString());
        //        DeserializeTreeView(sourceTreeView, stream, projectData.Importer);
        //        Debug.WriteLine("Desetialization done, start load target tree:" + DateTime.Now.ToLongTimeString());
        //        stream = new MemoryStream();
        //        using (XmlWriter writer = XmlWriter.Create(stream, GetXmlWriterSettings(false)))
        //        {
        //            XmlNode sourceNode = doc.DocumentElement.SelectSingleNode("TargetTreeView");
        //            sourceNode.WriteTo(writer);
        //            writer.Flush();
        //            stream.Position = 0;
        //        }

        //        Debug.WriteLine("Target tree extracted, start deserialization:" + DateTime.Now.ToLongTimeString());
        //        DeserializeTreeView(targetTreeView, stream, projectData.Importer);
        //        //Debug.WriteLine("Desetialization done, start load actions:" + DateTime.Now.ToLongTimeString());
        //        //stream = new MemoryStream();
        //        //using (XmlWriter writer = XmlWriter.Create(stream, GetXmlWriterSettings(false)))
        //        //{
        //        //    XmlNode sourceNode = doc.DocumentElement.SelectSingleNode("ArrayOfAction");
        //        //    if (sourceNode != null)
        //        //    {
        //        //        sourceNode.WriteTo(writer);
        //        //        writer.Flush();
        //        //        stream.Position = 0;
        //        //        projectData.ActionItemHandler.LoadXml(stream);
        //        //    }
        //        //}

        //        Debug.WriteLine("Load governance data:" + DateTime.Now.ToLongTimeString());
        //        stream = new MemoryStream();
        //        using (XmlWriter writer = XmlWriter.Create(stream))
        //        {
        //            XmlNode sourceNode = doc.DocumentElement.SelectSingleNode("GovernanceData");
        //            if (sourceNode != null)
        //            {
        //                sourceNode.WriteTo(writer);
        //                writer.Flush();
        //                stream.Position = 0;
        //                projectData.GovernanceData = GovernanceData.LoadXml(stream);
        //            }
        //        }

        //        Debug.WriteLine("Load Sitecollection templates:" + DateTime.Now.ToLongTimeString());
        //        stream = new MemoryStream();
        //        using (XmlWriter writer = XmlWriter.Create(stream))
        //        {
        //            XmlNode sourceNode = doc.DocumentElement.SelectSingleNode("ArrayOfSiteCollectionTemplate");
        //            if (sourceNode != null)
        //            {
        //                sourceNode.WriteTo(writer);
        //                writer.Flush();
        //                stream.Position = 0;
        //                TemplateDefinition.Instance.Clear();
        //                TemplateDefinition.Instance.SiteCollectionDefinitions = TemplateDefinition.LoadXml(stream);
        //            }
        //        }

        //        //XmlNode selectedSharePointItemId = doc.DocumentElement.SelectSingleNode(XmlNodeSelectedItemId);
        //        //if (selectedSharePointItemId != null)
        //        //{
        //        //    projectData.SelectedSharePointItemId = int.Parse(selectedSharePointItemId.InnerText);
        //        //}
        //    }
        //}

        protected void SendProgressInit(long stepNumber, string name)
        {
            if (ProgressInit != null)
            {
                ProgressInit(this, stepNumber, name);
                progressCount = 0;
            }
        }

        protected void SendProgressUpdate(long currentStep)
        {
            if (ProgressUpdate != null)
            {
                ProgressUpdate(this, currentStep);
            }
        }

        protected void SendProgressUpdateDone()
        {
            if (ProgressUpdateDone != null)
            {
                ProgressUpdateDone(this);
            }
        }

        XmlWriterSettings GetXmlWriterSettings(bool omitXmlDeclaration = true)
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Encoding = new UnicodeEncoding(false, false); // no BOM in a .NET string
            settings.Indent = true;
            settings.OmitXmlDeclaration = omitXmlDeclaration;
            settings.CheckCharacters = false;
            return settings;
        }


        protected void SaveData(Stream stream, TreeView sourceTreeView, TreeView targetTreeView, ProjectData data)
        {
            using (XmlWriter writer = XmlWriter.Create(stream, GetXmlWriterSettings(false))) //new XmlTextWriter(stream, System.Text.Encoding.Unicode);
            //XmlWriter writer = new XmlTextWriter(stream, System.Text.Encoding.Unicode);
            {
                writer.WriteStartDocument();
                // writing the main tag that encloses all node tags
                writer.WriteStartElement("ProjectPersistence");

                SendProgressInit(sourceTreeView.GetNodeCount(true), "Save source tree");

                var sb = SerializeTreeView(sourceTreeView, "SourceTreeView", data.Importer);

                writer.WriteRaw(sb.ToString());

                SendProgressInit(targetTreeView.GetNodeCount(true), "Save target tree");

                sb = SerializeTreeView(targetTreeView, "TargetTreeView", data.Importer);

                writer.WriteRaw(sb.ToString());

                //writer.WriteRaw(data.ActionItemHandler.Save().ToString());

                writer.WriteRaw(GovernanceData.Save(data.GovernanceData).ToString());

                writer.WriteRaw(TemplateDefinition.Save(TemplateDefinition.Instance.SiteCollectionDefinitions).ToString());

                writer.WriteRaw(ProjectParameter.Save(data.ProjectParameter).ToString());

                writer.WriteRaw(string.Format("<{0}>{1}</{0}>", XmlNodeProjectDefinitionVersion, ProjectData.CurrentDefinitionVersion));

                writer.WriteEndElement();
                writer.Flush();

            }
        }

        public abstract void Save(object sendTo, System.Windows.Forms.TreeView sourceTreeView, System.Windows.Forms.TreeView targetTreeView, ProjectData projectData);

        public abstract void Load(object readFrom, System.Windows.Forms.TreeView sourceTreeView, System.Windows.Forms.TreeView targetTreeView, ProjectData projectData);

        protected static MemoryStream CreateZipStream(Stream fileStream, string fileName)
        {
            MemoryStream memoryOutStream = new MemoryStream();
            ZipOutputStream zipStream = new ZipOutputStream(memoryOutStream);

            zipStream.SetLevel(3);  // 0..9 compress level

            ZipEntry entry = new ZipEntry(fileName);
            entry.DateTime = DateTime.Now;

            zipStream.PutNextEntry(entry);
            StreamUtils.Copy(fileStream, zipStream, new byte[4096]);
            zipStream.CloseEntry();

            zipStream.IsStreamOwner = false;
            zipStream.Close();

            memoryOutStream.Position = 0;
            return memoryOutStream;
        }

        protected static MemoryStream CreateUnzipStream(Stream zipFileStream)
        {
            MemoryStream memoryOutStream = new MemoryStream();
            ZipInputStream zipStream = new ZipInputStream(zipFileStream);

            var entry = zipStream.GetNextEntry();

            if (entry != null)
            {
                byte[] buffer = new byte[zipStream.Length];
                zipStream.Read(buffer, 0, (int)zipStream.Length);
                memoryOutStream.Write(buffer, 0, buffer.Length);
            }

            //StreamUtils.Copy(zipStream, memoryOutStream, new byte[4096]);

            zipStream.CloseEntry();
            zipStream.Close();

            return memoryOutStream;
        }
    }
}
